package ua.yandex.shad.tempseries;

import java.util.Arrays;
import java.util.Collections;

public class TemperatureSeriesAnalysis {

    private static final double MIN_TEMP = -273.0;

    private final Double[] temperatureSeries;
    private final Double[] sortedTemperatureSeries;

    private boolean validateSeries() {
        return temperatureSeries.length != 0;
    }
    private double[] toPrimitives(final Double[] d) {
        double[] array = new double[d.length];
        int i = 0;
        for (Double x : d) {
            array[i++] = x;
        }
        return array;
    }
    private Double[] toObjects(final double[] d) {
        Double[] array = new Double[d.length];
        int i = 0;
        for (double x : d) {
            array[i++] = x;
        }
        return array;
    }
    public TemperatureSeriesAnalysis(final double[] tempSeries) {
        this.temperatureSeries = toObjects(tempSeries);
        sortedTemperatureSeries = this.temperatureSeries.clone();
        Arrays.sort(sortedTemperatureSeries, Collections.reverseOrder());
    }

    public final double calcAverageTemp() {
        if (!validateSeries()) {
            return 0.0;
        }
        double sum = 0.0;
        for (double d : temperatureSeries) {
            sum += d;
        }
        return sum / temperatureSeries.length;
    }

    public final double calcTempDeviation() {
        if (!validateSeries()) {
            return 0.0;
        }
        double mean = calcAverageTemp();
        double squaredDifferences = 0.0;
        for (double d : temperatureSeries) {
            double diff = d - mean;
            squaredDifferences += diff * diff;
        }
        squaredDifferences /= temperatureSeries.length;
        double deviation = Math.sqrt(squaredDifferences);
        return deviation;
    }

    public final double findTempClosestToZero() {
        return findTempClosestToValue(0.0);
    }

    public final double findTempClosestToValue(final double tempValue) {
        if (!validateSeries()) {
            return 0.0;
        }
        int elemIndex = 0;
        double distance = Math.abs(sortedTemperatureSeries[0] - tempValue);
        for (int i = 1; i < sortedTemperatureSeries.length; i++) {
            double currElement = sortedTemperatureSeries[i];
            double currentDistance = Math.abs(currElement - tempValue);
            if (currentDistance < distance) {
                elemIndex = i;
                distance = currentDistance;
            }
            if (currentDistance > distance) {
                break;
            }
        }
        return sortedTemperatureSeries[elemIndex];
    }
    public final double[] findTempsLessThen(final double tempValue) {
        if (!validateSeries()) {
            return new double[0];
        }
        int lenght = sortedTemperatureSeries.length;
        int indexOfSlice = -1;
        for (int i = 0; i < lenght; i++) {
            if (sortedTemperatureSeries[i] < tempValue) {
                indexOfSlice = i;
                break;
            }
        }
        if (indexOfSlice == -1) {
            return new double[0];
        }
        Double[] res; 
        res = Arrays.copyOfRange(sortedTemperatureSeries, indexOfSlice, lenght);
        return toPrimitives(res);
    }
    public final double[] findTempsGreaterThen(final double tempValue) {
        if (!validateSeries()) {
            return new double[0];
        }
        int indexOfSlice = -1;
        for (int i = 0; i < sortedTemperatureSeries.length; i++) {
            if (sortedTemperatureSeries[i] < tempValue) {
                indexOfSlice = i;
                break;
            }
        }
        if (indexOfSlice == -1) {
            return toPrimitives(sortedTemperatureSeries);
        }
        Double[] res;
        res = Arrays.copyOfRange(sortedTemperatureSeries, 0, indexOfSlice);
        return toPrimitives(res);
    }
    public final double[] forecatsTemp(final double tempValue) {
        if (!validateSeries()) {
            return new double[0];
        }
        // Проверить на предмет температуры в массиве ниже минимальной 
        int length = sortedTemperatureSeries.length;
        if (sortedTemperatureSeries[length - 1] < MIN_TEMP) {
            String message = "You have a temperature that is beyond zero";
            throw new IllegalTemperatureValueException(message);
        }
        // Минимально необходимая длинна ряда
        final int minSeriesLength = 10;
        if (length < minSeriesLength) {
            throw new NotEnoughValuesToForecastException();
        }
        return new double[]{};
    }
}
